
function __G__TRACKBACK__(errorMessage)
    print("----------------------------------------")
    print("LUA ERROR: " .. tostring(errorMessage) .. "\n")
    print(debug.traceback("", 2))
    print("----------------------------------------")

    Router:getInstance():throwError(errorMessage .. '\n' .. debug.traceback("", 2));

end

DEBUG = 1
DEBUG_FPS = true
DEBUG_MEM = false
LOAD_DEPRECATED_API = false
LOAD_SHORTCODES_API = true
CONFIG_SCREEN_ORIENTATION = "landscape"
CONFIG_SCREEN_WIDTH  = 1600
CONFIG_SCREEN_HEIGHT = 750
CONFIG_SCREEN_AUTOSCALE = "FIXED_WIDTH"

require "framework.init"

local function pretty_print(t)
   local s = "return "
   local function save(t, indent)
      s = s .. indent .. '{\n'

      for k, v in pairs(t) do
         if type(k) ~= 'number' then
            s = s .. string.format(indent .. "\t[%q] = %q,\n", k, v)
         end
      end

      for i, v in ipairs(t) do
         save(v, indent .. '\t')
      end

      s = s .. indent .. '};\n'
   end

   save(t, '')

   return s
end

local router = Router:getInstance()

local Attribute = require("Attribute")()
local has_custom, err = pcall(require , "update.custom_Attribute")

if not has_custom then
    print('自定义脚本加载失败')
    print(err)
end

for k, v in pairs(Attribute) do
    if not v._custom then
        router:addType(k)
    end
end
router:addType("")--分割线
for k, v in pairs(Attribute) do
    if v._custom then
        router:addType(k)
    end
end

local lua_obj = {}
local lua_obj_index = 0

local function create(type)
    local ctor = Attribute[type].ctor
    local inst

    if ctor then
        inst = ctor() 
        function inst:getDescription()
            return type
        end 
        lua_obj_index = lua_obj_index + 1
        lua_obj[lua_obj_index] = inst

        --mark this obj is a custom type.
        inst:setUserObject(CCInteger:create(lua_obj_index))
    else
        inst = _G['XX'..type]:create()
    end

    return inst
end

local function find(inst)
    local ref = inst:getUserObject()
    if ref and ref.getValue then
        return lua_obj[ref:getValue()]
    else
        return inst
    end
end

function set(widget, name, value)
    local attrs = Attribute[find(widget):getDescription()]
    local accesser = attrs[name]

    if accesser then
        accesser.set(widget, value)
    end
end

function get(widget, name)
    local attrs = Attribute[find(widget):getDescription()]
    local accesser = attrs[name]
    if accesser then
        return accesser.get(widget)
    end
end

function onAddItem(item, type, name)
    local inst = create(type)

    local attr = Attribute[type]

    if attr.editor_setup then
        attr.editor_setup(inst)
    end

    inst:setName(name)
    item.parent:getWidget():addChild(inst)

    item:setWidget(inst)
end

local function choicesToArray(c)
    if not c then
        return ""
    end
    
    local a = {}
    for i, v in ipairs(c) do
        table.insert(a, v[1])
    end
    return table.concat(a, ';')
end

function onSelectedItem(item)
    if not item then return end
    
    local widget = find(item:getWidget())
    local type = widget:getDescription()

    router:clearProp()

    print(type)
    local attrs = Attribute[type]
    assert(attrs, type)

    for i, v in ipairs(attrs) do
        local ok, value = pcall(v.get, widget)
        if ok then
            router:addProp(v.name, v.type, value, choicesToArray(v.choices), v.readonly)
        else
            print(v.name, value)
        end
    end
end

function itemTableFromString(str)
    local err_msg = ""

    local function loadItem(t)
        local item = TreeItem:create()
        local widget = create(t.class)

        local attr = Attribute[t.class]
        assert(attr, 'not font Attribute for ' .. t.class)
        for k, v in pairs(t) do
            if type(v) ~= 'table' then
                if not attr[k] or not attr[k].set then
                    err_msg = err_msg .. '\n' .. k .. " not found for ".. t.class
                else
                    attr[k].set(widget, v)
                end
            end
        end

        item.name = widget:getName()
        item:setText(widget:getName())
        item:setChecked(widget:isVisible())
        item:setWidget(widget)

        for i, v in ipairs(t) do
            local subitem = loadItem(v)
            widget:addChild(subitem:getWidget())
            item:addChild(subitem)
        end    
        return item
    end

    local script = loadstring(str)
    if not script then
        print('no')
        script = loadstring('return {' .. str .. '}')
    end

    t = script()

    local ret = {}

    for i, v in ipairs(t) do
        local item = loadItem(v)
        item:getWidget():retain()
        table.insert(ret, item)
    end

    if #err_msg > 0 then
        router:throwError(err_msg)
    end

    return ret
end

function itemTableToString(itemtable)
    local function saveWidget(t, w)
        local widget = find(w:getWidget())
        local type = widget:getDescription()
        local attrs = Attribute[type]

        local info = {class=type}

        for i, v in ipairs(attrs) do
            local ok, s = pcall(v.get, widget)
            if ok then
                if s ~= v.def then
                    info[v.name] = s
                else
                    print('ignore', v.name, s, v.def)
                end
            else
                dump(v)
            end
        end

        for i=1, w:childrenCount() do
            local c = w:childrenAtIndex(i-1)
            saveWidget(info, c)
        end

        table.insert(t, info)
    end

    local out = {}

    for i, v in ipairs(itemtable) do
        saveWidget(out, v)
    end

    return pretty_print(out)
end

function string.findlast(s, pattern, plain)
    local curr = 0
    repeat
        local next = s:find(pattern, curr + 1, plain)
        if next then curr = next end
    until (not next)

    if curr > 0 then
        return curr
    end
end


local function getext(p)
    local i = p:findlast(".", true)
    if i then
        return p:sub(i)
    else
        return ""
    end
end

local function widgetToString(root)

    local function saveWidget(t, widget)
        local type = widget:getDescription()
        local attrs = Attribute[type]

        local info = {class=type}

        for i, v in ipairs(attrs) do
            local ok, s = pcall(v.get, widget)
            if ok then
                if s ~= v.def then
                    info[v.name] = s
                else
                    print('ignore', v.name, s, v.def)
                end
            else
                dump(v)
            end
        end

        local children = widget:getChildren()

        for i=1, widget:getChildrenCount() do
            saveWidget(info, children:objectAtIndex(i-1))
        end

        table.insert(t, info)
    end

    local out = {}

    saveWidget(out, root)

    return pretty_print(out)
end

function onOpen(filepath)
    local ext = getext(filepath)

    if ext == '.xx' then
        local f = io.open(filepath, 'r')
        local str = f:read('*a')
        f:close()
        return itemTableFromString(str)
    elseif ext == '.json' or ext == '.ExportJson' then
        return itemTableFromString(widgetToString(GUIReader:shareReader():widgetFromJsonFile(filepath)))
    elseif ext == '.csb' then
        return itemTableFromString(widgetToString(GUIReader:shareReader():widgetFromBinaryFile(filepath)))
    end
end

function onSave(itemtable, filepath)
    local f = io.open(filepath, 'w')
    f:write(itemTableToString(itemtable))
    f:close()
end

function getCode(itemtable)

    local s = 'local function setup_ui()\n'
    s = s .. '\t-- auto gen by XXEditor \n'
    s = s .. '\tlocal root = Widget:create()\n'

    local function saveWidget(w, parent)
        local widget = find(w:getWidget())
        local type = widget:getDescription()
        local attrs = Attribute[type]

        local real_name = w:getWidget():getName()
        local name = parent .. '_' .. real_name

        s = s .. string.format('\n\tlocal %s = %s:create()\n', name, type)
        if parent then
            s = s .. string.format('\t%s:addTo(%s)\n', name, parent)
            s = s .. string.format('\t%s.%s = %s\n', parent, real_name, name)
        end

        for i, v in ipairs(attrs) do
            local ok, value = pcall(v.get, widget)
            if ok then
                if value ~= v.def and v.name ~= 'class' then
                    s = s .. '\t' .. name .. ':' .. v.fmt(value) .. '\n'
                else
                    print('ignore', v.name, value, v.def)
                end
            else
                dump(v)
            end
        end

        for i=1, w:childrenCount() do
            saveWidget(w:childrenAtIndex(i-1), name)
        end
    end

    for i, v in ipairs(itemtable) do
        saveWidget(v, 'root')
    end

    s = s .. '\n\treturn root\nend'
    return s
end


function getCodeCpp(itemtable)

    local s = 'static Widget* setup_ui() {\n'
    s = s .. '\t// auto gen by XXEditor \n'
    s = s .. '\tWidget root = Widget::create();\n'

    local function saveWidget(w, parent)
        local widget = find(w:getWidget())
        local type = widget:getDescription()
        local attrs = Attribute[type]

        local real_name = w:getWidget():getName()
        local name = parent .. '_' .. real_name

        s = s .. string.format('\n\t%s* %s = %s::create();\n', type, name, type)
        if parent then
            s = s .. string.format('\t%s->addChild(%s);\n', parent, name)
        end

        for i, v in ipairs(attrs) do
            local ok, value = pcall(v.get, widget)
            if ok then
                if value ~= v.def and v.name ~= 'class' then
                    s = s .. '\t'.. name .. '->' .. v.fmt(value) .. ';\n'
                else
                    print('ignore', v.name, value, v.def)
                end
            else
                dump(v)
            end
        end

        for i=1, w:childrenCount() do
            saveWidget(w:childrenAtIndex(i-1), name)
        end
    end

    for i, v in ipairs(itemtable) do
        saveWidget(v, 'root')
    end

    s = s .. '\n\treturn root;\n}'
    return s
end